import { provide, ref, reactive } from "vue";
import type { Form, FormField, State } from "./types";
import { formInjectKey } from "./types";
import {
  Values,
  Rules,
  ValidatorError,
  ValidateError,
  ValidateErrorResult,
} from "../validate";
import { notEmpty } from "../utils";

import { useDebounceFn } from "@vueuse/core";

export default (values: Values, rules: Rules) => {
  const fields: Array<FormField> = [];
  //
  const addField = (field: FormField) => {
      if (field.name) fields.push(field);
    },
    removeField = (field: FormField) => {
      fields.splice(fields.indexOf(field), 1);
    };
  // 表单状态 state: normal | validating | success | failure
  const state = ref<State>("normal"),
    getRule = (name: string) => rules[name],
    /**
     * validate 验证方法
     *
     * 如果验证成功，则返回表单值。否则触发 ValidateError 异常。
     */
    validate = async () => {
      const result: Values = {},
        errors: ValidateErrorResult = {};
      state.value = "validating";
      for (const i of fields) {
        try {
          i.reset();
          result[i.name] = await i.validate();
        } catch (error) {
          errors[i.name] = (error as ValidatorError).messages;
        }
      }
      if (notEmpty(errors)) {
        state.value = "failure";
        throw new ValidateError(errors);
      }
      state.value = "success";
      return result;
    },
    /**
     * handleSubmit
     *
     */
    handleSubmit = (
      onSuccess: (values: Values) => void,
      onFailure?: (errors: ValidateError) => void
    ) =>
      useDebounceFn(async () => {
        try {
          onSuccess(await validate());
        } catch (error) {
          // setErrors(err.errors);
          onFailure?.(error as ValidateError);
        }
      }, 250),
    /**
     * reset
     */
    reset = () => {
      state.value = "normal";
      for (const item of fields) item.reset();
    },
    setErrors = (errors: ValidateErrorResult) => {
      for (const name in errors) {
        const item = fields.find((i) => i.name == name);
        if (item) item.setError(errors[name]);
      }
    };
  provide<Form>(formInjectKey, {
    addField,
    removeField,
    getRule,
    setErrors,
    state,
    values: reactive(values),
    rules: reactive(rules),
  });
  return {
    handleSubmit,
    validate,
    setErrors,
    reset,
    state,
  };
};
